#ifndef _sbrush_pantomime_h_
#define _sbrush_pantomime_h_

#include <set>
#include <map>
#include <memory>
#include <vector>

#include "sbrush_instrument.h"
#include "sms.h"
#include "sbrush_spectral_model_player.h"
#include "dag_threading.h"


namespace sbrush {
	
	class PantomimeError : public AcsError {
	public:
		PantomimeError(const std::string& msg);
		virtual ~PantomimeError() throw();
	};
	
//	class DuplicateTimestamp : public PantomimeError {
//	public:
//		DuplicateTimestamp();
//		virtual ~DuplicateTimestamp() throw();
//	};
	
	/* The pantomime is generally going to contain asynchronous, sequential time
	 * related information. For now we're hardcoding that to be an instrument
	 * state (probably won't change that in the future since instrument state is
	 * a pretty flexible object) and SMS_Data (probably will change that later).
	 * For now, we're going hold the data via it's timestamp rather than via an
	 * index.  This accounts for the asynchronous characteristics of the 
	 * Pantomime object, but probably produces some overhead having to deal with
	 * the recording device maintaining an accurate clock.  Probably not the
	 * end of the world though. */
	class Sample {
	public:
		Sample();
//		Sample(const Sample& copy);
//		Sample& operator=(const Sample& rhs);
		Sample(unsigned frame, const InstrumentState& state);
		virtual ~Sample() throw();
		InstrumentState instrumentState;
		unsigned smsFrame;
		
		static std::auto_ptr<Sample> inflate(std::istream& binaryStream);
		void serialize(std::ostream& binaryStream) const;
		
	private:
	};
	
	class Pantomime {		
		Pantomime();
		Pantomime(const Pantomime& copy);
		Pantomime& operator=(const Pantomime& rhs);
	public:
		Pantomime(const SpectralModel& spectralModel);
		virtual ~Pantomime();
		
		size_t size() const;
		void add(const Sample& sample);
		void add(std::auto_ptr<Sample> sample);
		const Sample& at(size_t idx) const;
		const SpectralModel& getSpectralModel() const;
		
		static std::auto_ptr<Pantomime> read(const char* filename);
		bool write(const char* filename) const;
		static std::auto_ptr<Pantomime> inflate(std::istream& binaryStream);
		void serialize(std::ostream& binaryStream) const;
		
	private:
		std::vector<Sample*> m_Samples;
		SpectralModel m_SpectralModel;
	};
	
	
	/* The archiver has a difficult job.  It needs to grab, and copy instrument
	 * states and sms data without bothering the sending thread. Both instrument
	 * states and sms data are variable sized and require dynamically allocated
	 * memory which can potentially slow down the sending thread. Hoping to 
	 * avoid rewriting *new*, .... */

	/* TODO: for now I'm accepting that (many) allocations will happen on the
	 * instrument's event thread. For now this is probably fine, but this should
	 * be addressed, especially for instruments with a higher sample rate.  This
	 * presents a tricky problem though, how to keep instrument states dynamic
	 * without require dynamic allocations. Truly, the solution is most likey
	 * to have some sort of memory bank used by the InstrumentState */
	enum PantomimeAction {
		Pantomime_Prepare = 0,
		Pantomime_Countdown = 1,
		Pantomime_Begin = 2,
		Pantomime_Cancelled = 3,
		Pantomime_Finish = 4
	};
	
	class PantomimeRecorder;
	typedef void (*pantomime_recorder_cb)(PantomimeRecorder* archiver, 
										  PantomimeAction action,
										  void* userData);
	
	/* TODO: create some sort of buffering mechanism to keep this from having
	 * to alloc memory for instrument states while capturing */
	class PantomimeRecorder : public InstrumentListener
	{
		class CountdownThread : public dag::Runnable {
		public:
			CountdownThread();
			virtual ~CountdownThread() throw();
			void startCountdown(PantomimeRecorder& recorder,
								unsigned countDown,
								double countDownDelta,
								pantomime_recorder_cb callback,
								void* userData);
			void runThread();
			
		private:
			unsigned m_CountDown;
			double m_CountDownDelta;
			PantomimeRecorder* m_Recorder;
			pantomime_recorder_cb m_Callback;
			void* m_UserData;
		};
		
		friend class CountdownThread;
	public:
		PantomimeRecorder();
		virtual ~PantomimeRecorder() throw();
		void setCallback(pantomime_recorder_cb callback, void* userData);
		void start(const SpectralModel& model, 
				   unsigned countdown, 
				   double countdownDelta);
		void setFinished();
		void cancel();
		std::auto_ptr<Pantomime> getPantomime() const;
		void instrumentCallback(const Instrument* instrument,
								const InstrumentState& state);
		void clear();
	private:
		
	private:
		void startRecording();
		
		mutable dag::Mutex m_StateMutex;
		std::map<size_t, InstrumentState*> m_Frame2State;
		std::set<InstrumentState*> m_States;
		bool m_DoCapture;
		pantomime_recorder_cb m_Callback;
		void* m_UserData;
		SpectralModelPlayer m_Player;
		const SpectralModel* m_SpectralModel;
		CountdownThread m_CountDownThread;
	};
}

#endif